package com.micro.modual.async.completion.realize;

import com.google.common.base.Stopwatch;
import com.micro.common.MicroConstant;
import com.micro.modual.async.completion.enums.CompletionRuleFlag;
import com.micro.modual.async.completion.executor.CompletionRuleExecutor;
import com.micro.modual.entity.CouponTemplate;
import com.micro.modual.mapper.CouponTemplateMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.RandomStringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * <h1>异步执行优惠券编号生成</h1>
 * @author LevelCoder
 * @version V1.0.0
 * @email LevelCoder@126.com
 * @date 2020/5/6
 */
@Slf4j
@Component
public class AsyncConstructCouponCode<T> implements CompletionRuleExecutor<T> {

    @Autowired
    private CouponTemplateMapper couponTemplateMapper;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Override
    public CompletionRuleFlag ruleConfig() {
        return CompletionRuleFlag.ASYNC_COUPON_CODE;
    }

    @Async("getAsyncCouponCodeExecutor")
    @Override
    public void computeRule(T t) {
        CouponTemplate couponTemplate = (CouponTemplate) t;
        Stopwatch stopwatch = Stopwatch.createStarted();
        Set<String> couponCodes = buildCouponCode(couponTemplate);
        //设置redis key
        String redisKey = String.format("%s%s", MicroConstant.RedisKeyPrefix.COUPON_TEMPLATE_CODE,couponTemplate.getId().toString());
        log.info("Push CouponCode To Redis:{}",redisTemplate.opsForList().rightPushAll(redisKey,couponCodes));
        couponTemplate.setAvailable((byte)1);
        couponTemplateMapper.updateByPrimaryKeySelective(couponTemplate);
        stopwatch.stop();
        log.info("Construct Coupon Code By Template Cost:{}ms",stopwatch.elapsed(TimeUnit.MILLISECONDS));
        //TODO:发送消息给运营人员可以使用优惠券模版
    }

    /**
     * 构造优惠券码(对应每一张优惠券 共计18位)
     * 前四位 产品线 + 类型
     * 中间六位 日期随机
     * 后八位 0~9随机数
     * @param couponTemplate {@link CouponTemplate} 优惠券模版实体
     * @return Set<String> 与Template.count相同个数的优惠券码
     */
    @SuppressWarnings("all")
    private Set<String> buildCouponCode(CouponTemplate couponTemplate){
        //创建一个定时器
        Stopwatch stopwatch = Stopwatch.createStarted();
        Set<String> result = new HashSet<>(couponTemplate.getCouponCount());
        String prifix4 = couponTemplate.getProductLine().toString() + couponTemplate.getTemplateCategory();
        String date = new SimpleDateFormat("yyMMdd").format(couponTemplate.getCreateTime());
        for(int i = 0 ;  i != couponTemplate.getCouponCount(); ++i){
            result.add(prifix4 + buildCouponCodeSuffix14(date));
        }
        //判断获取结果是否等于优惠券所定义的数量
        while (result.size() < couponTemplate.getCouponCount()){
            result.add(prifix4 + buildCouponCodeSuffix14(date));
        }
        //断言
        assert  result.size() == couponTemplate.getCouponCount();
        //停止定时器
        stopwatch.stop();
        //打印日志查看消耗多长时间
        log.info("Build Coupon Code Cost:{}ms",stopwatch.elapsed(TimeUnit.MILLISECONDS));
        return result;
    }

    /**
     * 构造优惠券码的后14位
     * @param date  创建优惠券的日期
     * @return 14位优惠券码
     */
    private String buildCouponCodeSuffix14(String date){
        //定义基准字符
        char[] bases = new char[]{'1','2','3','4','5','6','7','8','9'};
        //中间六位
        //
        List<Character> chars = date.chars().mapToObj(e -> (char)e).collect(Collectors.toList());
        //通过洗牌算法针对chars进行随机
        Collections.shuffle(chars);
        //通过chars流针对map进行转换位String字符串,传递的参数为chars组合 Collectors.joining()组合,把所有字符组合为一个字符串
        String mid6 = chars.stream().map(Object ::toString).collect(Collectors.joining());
        //后八位 第一位不为0的字符串 后7为为0-9的随机字符串
        String suffix8 = RandomStringUtils.random(1,bases)+RandomStringUtils.randomNumeric(7);

        return mid6 + suffix8;
    }


}
